package com.study.kt._01grammar._02oop

import org.omg.CORBA.portable.Delegate
import java.util.*
import kotlin.collections.ArrayList
import kotlin.properties.Delegates
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

/*
* 委托也是代理机制
*
* 原理：by关键字后面的对象会存储到类内部，编译器会实现类所需要的实现的所有方法，
*   并且将实现转移给委托对象实现
*
* 委托转换规则：
*   对于每个委托属性来说，kotlin编译器在底层会生成一个辅助属性，然后对原有属性的访问委托给辅助属性
*
* 属性委托应用：
*   1.延迟属性
*   2.可观测属性
*   3.非空属性
*   4.map属性
*   5.提供委托
* */
fun main() {
    testMapDelegate()
}

//--------------kotlin官方提供的几种委托-----------------------
// 延迟加载 by lazy: 用到的时候才加载 而且只会加载一次 是线程安全的、 只会创建一次，而且在用到时才创建
// lazy参数：指定是否加锁 默认为LazyThreadSafetyMode.SYNCHRONIZED 加锁
// LazyThreadSafetyMode. PUBLICATION:同一时间可以多个线程访问，但是使用第一个返回的值作为初始化的值
// LazyThreadSafetyMode. NONE:没有线程锁，非线程安全的
val name: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
    //代码块中最后一行为返回值赋值给name，而且该代码块只在创建时执行一次
    println("创建了name")
    "fauks"
}

class TheClass {
    // 延迟初始化属性
    // kotlin要求非空类型的属性必须在构造方法中初始化，但是实际使用不方便，例如使用findViewById、依赖注入等
    // 延迟初始化需要满足
    // 1. 只能用在类中声明的var属性
    // 2. 不能有自定义get、set方法
    // 3. 属性类型必须为非空不能为String? 而且不能睡原生数据类型，例如Int
    lateinit var name: String

    // 非空属性，无法在声明阶段赋值又不想赋一个无意义的值(0、false),在赋值前读取该字段会抛出异常
    var address by Delegates.notNull<String>()

    // 可观测属性:可以观测字段修改前后的值
    var age: Int by Delegates.observable(20) { prop, oldValue, newValue ->
        // 先赋值在执行代码块
        println("${prop.name},oldValue:$oldValue,newValue:$newValue")
    }
    var sorce: Float by Delegates.vetoable(1f) { prop, oldValue, newValue ->
        // 先执行代码块再赋值，如果返回true赋值成功，返回false不赋值
        when {
            newValue > oldValue -> true
            else -> false
        }
    }


    fun init() {
        name = "slx"
    }
}

// map委托：将属性存储到map中，一般用于JSON解析或者一些动态行为，可以使用map实例作为类中属性的委托
// map属性名需要与bean的字段名一致,此时的Map为只读
class Goods(map: Map<String, Any?>) {
    val name by map
    val price: Float by map
    val desc: String by map
    val time: Date by map
}

// 可读可写
class Goods2(map: MutableMap<String, Any?>) {
    var address: String by map
}

fun testMapDelegate() {
    val goods = Goods(
        mapOf(
            "name" to "apple",
            "price" to 12f,
            "desc" to "goods product",
            "time" to Date()
        )
    )
    println(goods.desc)
    println("--------")
    val map: MutableMap<String, Any?> = mutableMapOf(
        "address" to "beijing"
    )
    val goods2 = Goods2(map)
    println(goods2.address)
    println(map["address"])
    println("---------------")
    // 修改值
    goods2.address = "shanghai"
    println(goods2.address)
    println(map["address"])
}

// 类委托
fun testClassEntrust() {
    //类委托方式1
    val f = Father1()
    f.run()

    //类委托方式2
    val f2 = Father2(Son1())
    f2.run()
}

//类的委托机制
interface Run {
    fun run()
}

class Son1 : Run {
    override fun run() {
        println("son run")
    }
}

//类委托机制 by关键字，实现接口后发现与Son1有相同的实现，则把接口实现委托给Son1
class Father1 : Run by Son1()

//委托方式二
class Father2(son: Son1) : Run by son


//---------------属性委托----------------
//属性委托
fun testPropEntrust() {
    val bSon = BigSon()
    bSon.money = 100
    println(bSon.money)

    println(name)
    println(name)

    val myList = MyArrayList<String>()
    myList.add("fauks")

    val delegateTest = DelegateTest()
    println(delegateTest.d1)
    delegateTest.d2 = 12
}

//将字段的get、setter方法交给另一个类实现
class SmallFather {
    //get、set相当于 BigSon的 get、set方法
    // thisRef:被委托的对象本身，property被委托的属性
    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        println("son 使用 get方法：$thisRef,委托的属性名：${property.name}")
        return this.sonMoney - 50
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, i: Int) {
        println("son 使用 set方法")
        this.sonMoney = 100
    }

    var sonMoney: Int = 0
}

class BigSon {
    // 将money属性委托给SmallFather处理
    // 此时money属性无条件的委托给SmallFather 如果有些情况不想委托，或者可以控制委托与否
    // 就需要使用提供委托
    var money: Int by SmallFather()
}

// 属性委托方式二：上面使用get、set方法比较复杂，还有很多参数
class Delegate1 : ReadOnlyProperty<Any, String> {
    override fun getValue(thisRef: Any, property: KProperty<*>): String {
        return "通过ReadOnlyProperty实现属性委托，name:${property.name}"
    }
}

class Delegate2 : ReadWriteProperty<Any, Int> {
    override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {
        println("委托属性：${property.name} 委托值为 $value")
    }

    override fun getValue(thisRef: Any, property: KProperty<*>): Int {
        return 20
    }
}

// 使用委托
class DelegateTest {
    val d1: String by Delegate1()
    var d2: Int by Delegate2()
}

//使用委托机制，增强List的add方法（类似Java装饰者模式)
class MyArrayList<E>(val oriList: MutableList<E> = ArrayList<E>()) :
    MutableCollection<E> by oriList {
    override fun add(element: E): Boolean {
        println("添加一个元素")
        //内部直接调用MutableList的add方法
        return oriList.add(element)
    }
}
